#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "galois.h"
#include <sys/time.h>

#define BUFSIZE (65536)  /* 2 ^ 16 */
#define S_BUFSIZE (4096)
#define MULT 1
#define LOG 2
#define SHIFT 3
#define BITMATRIX 4
#define SPLITW8 5

#define COMP2 6
#define SCOMP2 7
#define RCOMP2 8

#define LAZYTABLE 9
#define LAZYSPLIT8 10
#define LAZYSPLIT16 11

#define talloc(type, num) (type *) malloc(sizeof(type)*(num))

extern int printer;
int printer;

void usage(char *s)
{
  fprintf(stderr, "usage: gf_basic_tester\\\nw\\\nmulttable|logtable|shift|bitmatrix|splitw8|comp2|scomp2|rcomp2|lazytable|lazysplit8|lazysplit16\\\nntrials-for-correctness\n");
  if (s != NULL) fprintf(stderr, "%s\n", s);
  exit(1);
}

main(int argc, char **argv)
{
  uint64_t w, alltype, nt, i, x, y, z, top, elt, q1, q2;
	uint64_t *x128, *y128, *z128, *elt128, *q128, *q228;
	uint64_t *lp1, *lp2, *lp3;
	uint64_t *lb1, *lb2;
  uint32_t *b1, *b2;
  uint32_t *ib1, *ib2, *ib3;
  uint16_t *sb1, *sb2, *sb3;
  struct timeval t1, t2;
  struct timezone tz;
  long t0, now;
  double total, tsec;
  unsigned char *cb1, *cb2, *cb3;
  int mtype;

  if (argc != 4) usage(NULL);

  w = atoi(argv[1]);
  if (!((w > 0 && w <= 32) || w == 64 || w == 128)) usage("w must be in [1,32] U [64] U [128]\n");

  if (strcmp(argv[2], "multtable") == 0) {
    mtype = MULT;
    if (galois_create_mult_tables(w) != 0) {
      printf("Cannot create multiplication tables for w = %d\n", w);
      exit(0);
    }
  } else if (strcmp(argv[2], "logtable") == 0) {
    if (galois_create_log_tables(w) != 0) {
      printf("Cannot create log tables for w = %d\n", w);
      exit(0);
    }
    mtype = LOG;
  } else if (strcmp(argv[2], "shift") == 0) {
    mtype = SHIFT;
  } else if (strcmp(argv[2], "bitmatrix") == 0) {
    mtype = BITMATRIX;
	} else if (strcmp(argv[2], "splitw8") == 0) {
		mtype = SPLITW8;
    if (!(w == 32 || w == 64 || w == 128)) {
      fprintf(stderr, "Splitw8 is only valid for w = {32, 64, 128}\n");
      exit(1);
    }
    if (w == 32 && galois_w32_create_split8_tables() != 0) {
      printf("Cannot create split8_table tables for w = %d\n", w);
      exit(0);
    }
		if (w == 64 && galois_w64_create_split8_tables() != 0) {
      printf("Cannot create split8_table tables for w = %d\n", w);
      exit(0);
		}
		if (w == 128 && galois_w128_create_split8_tables() != 0) {
      printf("Cannot create split8_table tables for w = %d\n", w);
      exit(0);
		}
  } else if (strcmp(argv[2], "comp2") == 0) {
		mtype = COMP2;
	} else if (strcmp(argv[2], "scomp2") == 0) {
		mtype = SCOMP2;
	} else if (strcmp(argv[2], "rcomp2") == 0) {
		mtype = RCOMP2;
	} else if (strcmp(argv[2], "lazytable") == 0) {
		mtype = LAZYTABLE;
	} else if (strcmp(argv[2], "lazysplit8") == 0) {
		if (!(w == 32 || w == 64 || w == 128)) {
			fprintf(stderr, "Lazy split8 tables are only implemented for w == {32, 64, 128}");
			exit(1);
		}
		mtype = LAZYSPLIT8;
	} else if (strcmp(argv[2], "lazysplit16") == 0) {
		if (!(w == 32 || w == 64 || w == 128)) {
			fprintf(stderr, "Lazy split16 tables are only implemented for w == {32, 64, 128}");
			exit(1);
		}
		mtype = LAZYSPLIT16;
	} else {
		usage("arg 2 must be multtable, logtable, shift, bitmatrix, splitw8, comp2, scomp2, rcomp2, lazytable, lazysplit8 or lazysplit16\n");
	}
 
  nt = atoi(argv[3]);
	if (nt <= 0) usage("arg 3 must be a positive integer\n");

  t0 = time(0);
  srand48(0); 
  printf("W: %d\n", w);
  printf("Method: %s\n", argv[2]);
  printf("Seeding random number generator with %u\n", t0);
  
  top = (1 << w);

	if (w == 4 || w == 8 || w == 16 || w == 32 || w == 64 || w == 128) {
		cb1 = talloc(unsigned char, BUFSIZE);
		cb2 = talloc(unsigned char, BUFSIZE);
		cb3 = talloc(unsigned char, BUFSIZE);
		for (i = 0; i < BUFSIZE; i++) cb1[i] = lrand48() % 256;
		for (i = 0; i < BUFSIZE; i++) cb2[i] = lrand48() % 256;
	}

	if (w < 64 && mtype < 7) {
		b1 = talloc(uint32_t, S_BUFSIZE);
		b2 = talloc(uint32_t, S_BUFSIZE);
		if (w < 32 ) {
			for (i = 0; i < S_BUFSIZE; i++) b1[i] = lrand48() % top;
			for (i = 0; i < S_BUFSIZE; i++) b2[i] = lrand48() % top;
		} else {
			for (i = 0; i < S_BUFSIZE; i++) b1[i] = lrand48();
			for (i = 0; i < S_BUFSIZE; i++) b2[i] = lrand48();
		}

		printf("Doing %u trials for single-operation correctness.\n", nt);
		for (i = 0; i < nt; i++) {
			if (w < 32) {
				x = lrand48()%(top-1)+1;
				y = lrand48()%(top-1)+1;
			} else {
				for (x = 0; x == 0; x = lrand48()) ;
				for (y = 0; y == 0; y = lrand48()) ;
			}
			switch (mtype) {
				case MULT:    z = galois_multtable_multiply(x, y, w);
											q1 = galois_multtable_divide(z, x, w); 
											q2 = galois_multtable_divide(z, y, w); 
											break;
				case LOG:			z = galois_logtable_multiply(x, y, w);
											q1 = galois_logtable_divide(z, x, w); 
											q2 = galois_logtable_divide(z, y, w); 
											break;
				case BITMATRIX:   z = galois_bitmatrix_multiply(x, y, w);
													q1 = galois_bitmatrix_divide(z, x, w); 
													q2 = galois_bitmatrix_divide(z, y, w); 
													break;
				case SHIFT:   z = galois_shift_multiply(x, y, w);
											q1 = galois_shift_divide(z, x, w); 
											q2 = galois_shift_divide(z, y, w); 
											break;
				case SPLITW8: z = galois_w32_split8_table_multiply(x, y);
											q1 = galois_bitmatrix_divide(z, x, w); 
											q2 = galois_bitmatrix_divide(z, y, w); 
											break;
				case COMP2:		z = galois_w32_composite_k2_multiply(x, y);
											q1 = galois_w32_composite_k2_divide(z, x);
											q2 = galois_w32_composite_k2_divide(z, y);
											break;
			}
			if (q1 != y) {
				fprintf(stderr, "Failed test: %u * %u = %u, but %u / %u = %u\n",
						x, y, z, z, x, q1);
				exit(1);
			}
			if (q2 != x) {
				fprintf(stderr, "Failed test: %u * %u = %u, but %u / %u = %u\n",
						y, x, z, z, y, q2);
				exit(1);
			}
			switch (mtype) {
				case MULT:	z = galois_multtable_divide (1, x, w);
										q1 = galois_multtable_divide (1, z, w);
										break;
				case LOG:		z = galois_logtable_divide(1, x, w);
										q1 = galois_logtable_divide (1, z, w);
										break;
				case BITMATRIX: z = galois_bitmatrix_divide(1, x, w);
										q1 = galois_bitmatrix_divide (1, z, w);
										break;
				case SHIFT: z = galois_shift_divide(1, x, w);
										q1 = galois_shift_divide (1, z, w);
										break;
				case SPLITW8: z = galois_bitmatrix_divide(1, x, w);
										q1 = galois_bitmatrix_divide (1, z, w);
										break;
				case COMP2:	z = galois_w32_composite_k2_divide (1, x);
										q1 = galois_w32_composite_k2_divide (1, z);
			}
			if (q1 != x) {
				fprintf(stderr, "Failed test: gf_inverse(%u) = %u, but gf_inverse(%u) = %u\n",
						x, z, z, q1);
				exit(1);
			}
		}
		printf("Passed Single-Operations Correctness Tests.\n");
		printf("\n");

	} else if (w == 64 && mtype < 9 && mtype != 7) {
		lb1 = talloc(uint64_t, S_BUFSIZE);
		lb2 = talloc(uint64_t, S_BUFSIZE);

		for (i = 0; i < S_BUFSIZE; i++) {
			lb1[i] = lrand48();
			lb1[i] <<= 32;
			lb1[i] += lrand48();
		}
		for (i = 0; i < S_BUFSIZE; i++) {
			lb2[i] = lrand48();
			lb2[i] <<= 32;
			lb2[i] += lrand48();
		}

		printf("Doing %u trials for single-operation correctness.\n", nt);
		for (i = 0; i < nt; i++) {
				for (x = 0; x == 0;) {
					x = lrand48();
					x <<= 32;
					x +=lrand48();
				}
				for (y = 0; y == 0;) {
					y = lrand48();
					y <<= 32;
					y +=lrand48();
				}
			switch (mtype) {
				case BITMATRIX:   z = galois_w64_bitmatrix_multiply(x, y);
													q1 = galois_w64_bitmatrix_divide(z, x); 
													q2 = galois_w64_bitmatrix_divide(z, y); 
													break;
				case SHIFT:   z = galois_w64_shift_multiply(x, y);
											q1 = galois_w64_bitmatrix_divide(z, x); 
											q2 = galois_w64_bitmatrix_divide(z, y); 
											break;
				case SPLITW8: z = galois_w64_split8_table_multiply(x, y);
											q1 = galois_w64_bitmatrix_divide(z, x); 
											q2 = galois_w64_bitmatrix_divide(z, y); 
											break;
				case COMP2:		z = galois_w64_composite_k2_multiply(x, y);
											q1 = galois_w64_composite_k2_divide(z, x);
											q2 = galois_w64_composite_k2_divide(z, y);
											break;
				case RCOMP2:  z = galois_w64_recursive_composite_k2_multiply (x, y);
											z = galois_w64_recursive_composite_k2_divide (z, x);
											z = galois_w64_recursive_composite_k2_divide (z, y);
											break;
			}

			if (q1 != y) {
				fprintf(stderr, "Failed test: %lu * %lu = %lu, but %lu / %lu = %lu\n",
						x, y, z, z, x, q1);
				exit(1);
			}
			if (q2 != x) {
				fprintf(stderr, "Failed test: %lu * %lu = %lu, but %lu / %lu = %lu\n",
						y, x, z, z, y, q2);
				exit(1);
			}
			switch (mtype) {
				case BITMATRIX: z = galois_w64_bitmatrix_divide(1, x);
										q1 = galois_w64_bitmatrix_divide (1, z);
										break;
				case SHIFT: z = galois_w64_bitmatrix_divide(1, x);
										q1 = galois_w64_bitmatrix_divide (1, z);
										break;
				case SPLITW8: z = galois_w64_bitmatrix_divide(1, x);
										q1 = galois_w64_bitmatrix_divide (1, z);
										break;
				case COMP2:	z = galois_w64_composite_k2_divide (1, x);
										q1 = galois_w64_composite_k2_divide (1, z);
										break;
				case RCOMP2: z = galois_w64_recursive_composite_k2_divide (1, x);
									   z = galois_w64_recursive_composite_k2_divide (1, z);
										 break;
			}
			if (q1 != x) {
				fprintf(stderr, "Failed test: gf_inverse(%u) = %u, but gf_inverse(%u) = %u\n",
						x, z, z, q1);
				exit(1);
			}
		}
		printf("Passed Single-Operations Correctness Tests.\n");
		printf("\n");
	} else if (w == 128 && mtype < 9 && mtype != 7) {
		lb1 = talloc(uint64_t, S_BUFSIZE);
		lb2 = talloc(uint64_t, S_BUFSIZE);

		x128 = talloc(uint64_t, 2);
		y128 = talloc(uint64_t, 2);

		for (i = 0; i < S_BUFSIZE; i++) {
			lb1[i] = lrand48();
			lb1[i] <<= 32;
			lb1[i] += lrand48();
		}
		for (i = 0; i < S_BUFSIZE; i++) {
			lb2[i] = lrand48();
			lb2[i] <<= 32;
			lb2[i] += lrand48();
		}

		printf("Doing %u trials for single-operation correctness.\n", nt);
		for (i = 0; i < nt; i++) {
				for (x128[0] = 0; x128[0] == 0;) {
					x128[0] = lrand48();
					x128[0] <<= 32;
					x128[0] +=lrand48();
				}
				for (x128[1] = 0; x128[1] == 0;) {
					x128[1] = lrand48();
					x128[1] <<= 32;
					x128[1] +=lrand48();
				}
				for (y128[0] = 0; y128[0] == 0;) {
					y128[0] = lrand48();
					y128[0] <<= 32;
					y128[0] +=lrand48();
				}
				for (y128[1] = 0; y128[1] == 0;) {
					y128[1] = lrand48();
					y128[1] <<= 32;
					y128[1] +=lrand48();
				}
			switch (mtype) {
				case BITMATRIX:   z128 = galois_w128_bitmatrix_multiply(x128, y128);
													q128 = galois_w128_bitmatrix_divide(z128, x128); 
													q228 = galois_w128_bitmatrix_divide(z128, y128); 
													break;
				case SHIFT:   z128 = galois_w128_shift_multiply(x128, y128);
											q128 = galois_w128_bitmatrix_divide(z128, x128); 
											q228 = galois_w128_bitmatrix_divide(z128, y128); 
											break;
				case SPLITW8: z128 = galois_w128_split8_table_multiply(x128, y128);
											q128 = galois_w128_bitmatrix_divide(z128, x128); 
											q228 = galois_w128_bitmatrix_divide(z128, y128); 
											break;
				case COMP2:		z128 = galois_w128_composite_k2_multiply(x128, y128);
											q128 = galois_w128_composite_k2_divide(z128, x128);
											q228 = galois_w128_composite_k2_divide(z128, y128);
											break;
				case RCOMP2:  z128 = galois_w128_recursive_composite_k2_multiply (x128, y128);
											q128 = galois_w128_recursive_composite_k2_divide (z128, x128);
											q228 = galois_w128_recursive_composite_k2_divide (z128, y128);
											break;
			}

			for (i = 0; i < 2; i++) {
				if (q128[i] != y128[i]) {
					fprintf(stderr, "Failed test: %lu * %lu = %lu, but %lu / %lu = %lu\n",
							x, y128[i], z128[i], z128[i], x128[i], q128[i]);
					exit(1);
				}
				if (q228[i] != x128[i]) {
					fprintf(stderr, "Failed test: %lu * %lu = %lu, but %lu / %lu = %lu\n",
							y128[i], x128[i], z128[i], z128[i], y128[i], q228[i]);
					exit(1);
				}
			}
			free (z128);
			free (q128);
			free (q228);
			switch (mtype) {
				case BITMATRIX: z128 = galois_w128_bitmatrix_inverse(x128);
												q128 = galois_w128_bitmatrix_inverse (z128);
												break;
				case SHIFT: z128 = galois_w128_bitmatrix_inverse(x128);
										q128 = galois_w128_bitmatrix_inverse (z128);
										break;
				case SPLITW8: z128 = galois_w128_bitmatrix_inverse(x128);
											q128 = galois_w128_bitmatrix_inverse (z128);
										break;
				case COMP2:	z128 = galois_w128_composite_k2_inverse (x128);
										q128 = galois_w128_composite_k2_inverse (z128);
										break;
				case RCOMP2: z128 = galois_w128_recursive_composite_k2_inverse (x128);
									   z128 = galois_w128_recursive_composite_k2_inverse (z128);
										 break;
			}
			for (i = 0; i < 2; i++) {
				if (q128[i] != x128[i]) {
					fprintf(stderr, "Failed test: gf_inverse(%u) = %u, but gf_inverse(%u) = %u\n",
							x128[i], z128[i], z128[i], q128[i]);
					exit(1);
				}
			}
		}
		printf("Passed Single-Operations Correctness Tests.\n");
		printf("\n");
	}

/*
	if (w == 8 && mtype == LOG) {
		printf("Doing galois_w08_logtable_region_multiply correctness test.\n");
		for (x = 0; x < 10; x++) {

			/* First, test with r2 specified, and add = 0 */
			/*
			for (elt = 0; elt == 0; elt = lrand48()%top);
			galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, (unsigned char *) cb3, 0);
			for (i = 0; i < BUFSIZE; i++) {
				if (galois_single_multiply(cb1[i], elt, w) != cb3[i]) {
					printf("Failed test (r2 != NULL, add == 0): %u * %u = %u, but it is %u in cb3\n", 
							cb1[i], elt, galois_single_multiply(cb1[i], elt, w), cb3[i]);
					exit(1);
				}
			}

			/* Next, test with r2 = NULL */
			/*
			for (elt = 0; elt == 0; elt = lrand48()%top);
			galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, (unsigned char *) cb3, 0);
			elt = galois_single_divide(1, elt, w);
			galois_w08_logtable_region_multiply((unsigned char *) cb3, elt, BUFSIZE, NULL, 0);
			for (i = 0; i < BUFSIZE; i++) {
				if (cb1[i] != cb3[i]) {
					printf("Failed test (r2 == NULL): %u != %u\n", cb1[i], cb3[i]);
					exit(1);
				}
			}

			/* Finally, test with r2 specified, and add = 1 */
			/*

			for (elt = 0; elt == 0; elt = lrand48()%top);
			for (i = 0; i < BUFSIZE; i++) cb3[i] = cb2[i];
			galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, (unsigned char *) cb3, 1);
			for (i = 0; i < BUFSIZE; i++) {
				if (cb3[i] != (cb2[i] ^ galois_single_multiply(cb1[i], elt, w))) {
					printf("Failed test (r2 != NULL && add == 1): (%u * %u) ^ %u = %u.  cb3 = %u\n", 
							cb1[i], elt, cb2[i], (galois_single_multiply(cb1[i], elt, w)^cb2[i]), cb3[i]);
					exit(1);
				}
			}
		}
		galois_w08_logtable_region_multiply((unsigned char *) cb1, 0, BUFSIZE, (unsigned char *) cb3, 0);
		for (i = 0; i < BUFSIZE; i++) {
			if (cb3[i] != 0) {
				printf("Failed multiply by zero test.  Byte %d = %d\n", i, cb3[i]);
				exit(1);
			}
		}
		printf("Passed galois_w08_logtable_region_multiply correctness test.\n");
		printf("\n");
	}
	*/

	if (w < 9 && mtype < 7 ) {
		printf("Speed Test #1: 10 Seconds of Multiply operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case MULT:    for (i = 0; i < S_BUFSIZE; i++) galois_w08_multtable_multiply(b1[i], b2[i], w); break;
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w08_logtable_multiply(b1[i], b2[i], w); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_multiply(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w08_composite_k2_multiply(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_multiply(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w < 17 && mtype < 7) {
		printf("Speed Test #1: 10 Seconds of Multiply operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case MULT:    for (i = 0; i < S_BUFSIZE; i++) galois_w16_multtable_multiply(b1[i], b2[i], w); break;
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w16_logtable_multiply(b1[i], b2[i], w); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_multiply(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w16_composite_k2_multiply(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_multiply(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w < 33 && mtype < 7) {
		printf("Speed Test #1: 10 Seconds of Multiply operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w32_logtable_multiply(b1[i], b2[i], w); break;
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i++) galois_w32_split8_table_multiply(b1[i], b2[i]); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_multiply(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w32_composite_k2_multiply(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_multiply(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);

	} else if (w == 64 && mtype < 9 && mtype != 7) {
		printf("Speed Test #1: 10 Seconds of Multiply operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_split8_table_multiply(lb1[i], lb2[i]); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_bitmatrix_multiply(lb1[i], lb2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_w64_shift_multiply(lb1[i], lb2[i]); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_composite_k2_multiply(lb1[i], lb2[i]); break;
				case RCOMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_recursive_composite_k2_multiply(lb1[i], lb2[i]); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w == 128 && mtype < 9 && mtype != 7) {
		printf("Speed Test #1: 10 Seconds of Multiply operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i+=2) {
													z128 = galois_w128_split8_table_multiply(lb1+i, lb2+i); 
													free (z128);
												}
												break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i+=2) {
														z128 = galois_w128_bitmatrix_multiply(lb1+i, lb2+i); 
														free (z128);
													}
													break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i+=2) {
											z128 = galois_w128_shift_multiply(lb1+i, lb2+i); 
											free (z128);
										}
										break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i+=2) {
												z128 = galois_w128_composite_k2_multiply(lb1+i, lb2+i); 
												free (z128);
											}
											break;
				case RCOMP2:   for (i = 0; i < S_BUFSIZE; i+=2) {
												 z128 = galois_w128_recursive_composite_k2_multiply(lb1+i, lb2+i); 
												 free (z128);
											 }
											 break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	}

	tsec = 0;
	tsec += t2.tv_usec;
	tsec -= t1.tv_usec;
	tsec /= 1000000.0;
	tsec += t2.tv_sec;
	tsec -= t1.tv_sec;
	if (w == 128) {
		total *= S_BUFSIZE/2;
	} else {
		total *= S_BUFSIZE;
	}
	printf("Speed Test #1: %.5lf Mega Multiplies per second\n", total/tsec/1024.0/1024.0);


	if (w < 9 && mtype < 7 ) {
		printf("Speed Test #1: 10 Seconds of Divide operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case MULT:    for (i = 0; i < S_BUFSIZE; i++) galois_w08_multtable_divide(b1[i], b2[i], w); break;
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w08_logtable_divide(b1[i], b2[i], w); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_divide(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w08_composite_k2_divide(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_divide(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w < 17 && mtype < 7) {
		printf("Speed Test #1: 10 Seconds of Divide operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case MULT:    for (i = 0; i < S_BUFSIZE; i++) galois_w16_multtable_divide(b1[i], b2[i], w); break;
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w16_logtable_divide(b1[i], b2[i], w); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_divide(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w16_composite_k2_divide(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_divide(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w < 33 && mtype < 7) {
		printf("Speed Test #1: 10 Seconds of Divide operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case LOG:     for (i = 0; i < S_BUFSIZE; i++) galois_w32_logtable_divide(b1[i], b2[i], w); break;
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_divide(b1[i], b2[i], w); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_bitmatrix_divide(b1[i], b2[i], w); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w32_composite_k2_divide(b1[i], b2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_shift_divide(b1[i], b2[i], w); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);

	} else if (w == 64 && mtype < 9 && mtype != 7) {
		printf("Speed Test #1: 10 Seconds of Divide operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_split8_table_divide(lb1[i], lb2[i]); break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_bitmatrix_divide(lb1[i], lb2[i]); break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i++) galois_w64_bitmatrix_divide(lb1[i], lb2[i]); break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_composite_k2_divide(lb1[i], lb2[i]); break;
				case RCOMP2:   for (i = 0; i < S_BUFSIZE; i++) galois_w64_recursive_composite_k2_divide(lb1[i], lb2[i]); break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	} else if (w == 128 && mtype < 9 && mtype != 7) {
		printf("Speed Test #1: 10 Seconds of Divide operations\n");
		t0 = time(0);
		gettimeofday(&t1, &tz);
		total = 0;
		while (time(0) - t0 < 10) {
			switch (mtype) {
				case SPLITW8:   for (i = 0; i < S_BUFSIZE; i+=2) {
													z128 = galois_w128_bitmatrix_divide(lb1+i, lb2+i); 
													free (z128);
												}
												break;
				case BITMATRIX:   for (i = 0; i < S_BUFSIZE; i+=2) {
														z128 = galois_w128_bitmatrix_divide(lb1+i, lb2+i); 
														free (z128);
													}
													break;
				case SHIFT: for (i = 0; i < S_BUFSIZE; i+=2) {
											z128 = galois_w128_bitmatrix_divide(lb1+i, lb2+i); 
											free (z128);
										}
										break;
				case COMP2:   for (i = 0; i < S_BUFSIZE; i+=2) {
												z128 = galois_w128_composite_k2_divide(lb1+i, lb2+i); 
												free (z128);
											}
											break;
				case RCOMP2:   for (i = 0; i < S_BUFSIZE; i+=2) {
												 z128 = galois_w128_recursive_composite_k2_divide(lb1+i, lb2+i); 
												 free (z128);
											 }
											 break;
			}
			total++;
		}
		gettimeofday(&t2, &tz);
	}

	tsec = 0;
	tsec += t2.tv_usec;
	tsec -= t1.tv_usec;
	tsec /= 1000000.0;
	tsec += t2.tv_sec;
	tsec -= t1.tv_sec;
	if (i == 128) {
		total *= S_BUFSIZE/2;
	} else {
		total *= S_BUFSIZE;
	}
	printf("Speed Test #2: %.5lf Mega Divides per second\n", total/tsec/1024.0/1024.0);

	printf("\n");

/*
	if (w == 8 && mtype == LOG) {
		printf("Doing 10 seconds worth of region_multiplies - Three tests:\n");
		printf("   Test 0: Overwrite initial region\n");
		printf("   Test 1: Products to new region\n");
		printf("   Test 2: XOR products into second region\n\n");
		for (i = 0; i < 3; i++) {
			t0 = time(0);
			gettimeofday(&t1, &tz);
			total = 0;
			while (time(0) - t0 < 10) {
				for (x = 0; x < 10; x++) {
					for (elt = 0; elt == 0; elt = lrand48()%top) ;
					if (i == 0) {
						galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, NULL, 0);          
					} else if (i == 1) {
						galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, (unsigned char *) cb3, 0);
					} else {            
						galois_w08_logtable_region_multiply((unsigned char *) cb1, elt, BUFSIZE, (unsigned char *) cb3, 1);
					}
					total += BUFSIZE;
				}
			}
			gettimeofday(&t2, &tz);

			tsec = 0;
			tsec += t2.tv_usec;
			tsec -= t1.tv_usec;
			tsec /= 1000000.0;
			tsec += t2.tv_sec;
			tsec -= t1.tv_sec;
			printf("   Test %u: %.5lf Megabytes of Multiplies per second\n", i, total/tsec/1024.0/1024.0);
		}
	}
	*/

	return 0;
}

